#include "resman.h"
#include "resource_file.h"
#include "graphics/surface.h"

#include "num/crc32.h"
#include "util/mmgr.h"					// Needs to come in early
#include "util/common_macros.h"		    // deletion, etc
#include "util/log.h"		    // deletion, etc

#include <algorithm>
#include <cassert>
/// different resource types

using namespace res;

ResMan&	ResMan::Instance()
{
	static ResMan	Inst;
	return Inst;
}

ResMan::ResMan() : m_IsForceSkipInit(false)
{
}

ResMan::~ResMan()
{
	Flush();
}

ResourceFile*		ResMan::GetResource(const std::string& _Name)
{
	ResourceFile* pRes = m_ResourceMap.GetElement(_Name);
	if (!pRes)
	{
		pRes = LoadResource(_Name);
	}
	return pRes;
}

bool			ResMan::ResourceExists(const std::string& _Name)
{
	return m_ResourceMap.ElementExists(_Name);
}


ResourceFile*		ResMan::LoadResource(const std::string& _Name)
{
	ResourceCreator*	pResCreate = GetCreatorFromName(_Name);
	if (!pResCreate || !pResCreate->pFunc)
	{
		_LOG(MSG_ERROR, "Unable to find loader function for resource " << _Name);
		return NULL;
	}

	// create resource
	ResourceFile* pRes = pResCreate->pFunc();
	if (!pRes)
	{
		_LOG(MSG_ERROR, "Unable to find create resource " << _Name);
		return NULL;
	}
	// start loading process
	if (!pRes->Load(_Name))
	{
		UTIL_SAFE_DELETE(pRes);
		_LOG(MSG_ERROR, "Error loading resource " << _Name);
		return NULL;
	}
	
	// add to map
	m_ResourceMap.AddElement(_Name, pRes);

	// return 
	return pRes;
}

void			ResMan::Flush()
{
	m_ResourceMap.ClearSet();
}

void			ResMan::FlushResource(ResourceFile* pRes, void* pData)
{
	if (pRes)
	{
		pRes->ReleaseResource();
		pRes->InitializeResource();
	}
}

void			ResMan::ReleaseResource(const std::string& Name)
{
	ResourceFile*	pRes = m_ResourceMap.GetElement(Name);
	if (pRes)
	{
		pRes->ReleaseResource();
	}
}



void			ResMan::ReleaseResource(ResourceFile* pRes, void* pData)
{
	if (pRes)
	{
		pRes->ReleaseResource();
	}
}

void			ResMan::ReinitResource(ResourceFile* pRes, void* pData)
{
	if (pRes)
	{
		pRes->InitializeResource();
	}
}


void			ResMan::ReleaseAll()
{
	m_ResourceMap.ForEach(ReleaseResource, NULL);
}

void			ReleaseExceptUI();

void			ResMan::ReinitAll()
{
	m_ResourceMap.ForEach(ReinitResource, NULL);
}

void			ResMan::FlushData()
{
	m_ResourceMap.ForEach(FlushResource, NULL);
}


std::string		ResMan::GetExtension(const std::string& _Name)
{
	assert(!_Name.empty() && "Getting extension from illegal string!");
	// returns the string "tga" from either "test.tga", or the pure extension "tga"
	return _Name.substr( _Name.find_last_of( "." ) + 1 );
}


ResMan::ResourceCreator*	ResMan::GetCreatorFromName(const std::string& _Name)
{
	return m_CreatorMap.GetElement(GetExtension(_Name));
}

void			ResMan::AddResourceConstructor(CreateResFunc _Func, const std::string& _Ext)
{
	ResourceCreator*	pResCreate = GetCreatorFromName(_Ext);
	if (pResCreate)
	{
		_LOG(MSG_WARNING, "Double register extension " << _Ext << " in resource manager");

		pResCreate->pFunc = _Func;
		pResCreate->AltPaths.resize(0);
		return;
	}
	// get the key
	ResourceCreator*	pCreate = new ResourceCreator;
	pCreate->pFunc = _Func;
	// add to set
	m_CreatorMap.AddElement(_Ext, pCreate);
	_LOG(MSG_INIT, "Register extension " << _Ext << " in resource manager");
}

void			ResMan::AddAlternativePath(const std::string& Ext, const std::string& AltPath)
{
	int j=0;
}

static bool SortBySize(Surface* pA, Surface* pB)
{
	return pA->GetSize() < pB->GetSize();
}

void			ResMan::FlushTextures()
{
	unsigned int c= m_ResourceMap.GetElementC();
	for (unsigned int i=0; i<c; i++)
	{
		ResourceFile*	pFile = m_ResourceMap.GetElementByIndex(i);
		if (pFile->GetFileType() != ResourceFile::RES_SURFACE) continue;
//		if (pFile->GetState() != ResourceFile::RS_READY) continue;
		pFile->ReleaseResource();
	}
}

void			ResMan::FlushTexturesExceptUI()
{
	unsigned int c= m_ResourceMap.GetElementC();
	for (unsigned int i=0; i<c; i++)
	{
		ResourceFile*	pFile = m_ResourceMap.GetElementByIndex(i);
		if (pFile->GetFileType() != ResourceFile::RES_SURFACE) continue;
//		if (pFile->GetState() != ResourceFile::RS_READY) continue;
		
		const char*     File    = pFile->GetName().c_str();
		size_t          SL      = strlen(File);

		if(SL > 6 )
		{
			
			char	        FolderName[]   = { 
										static_cast<char>(tolower(File[0])),
										static_cast<char>(tolower(File[1])),
										static_cast<char>(tolower(File[2])),
										static_cast<char>(tolower(File[3])),
										static_cast<char>(tolower(File[4])),
										static_cast<char>(tolower(File[5])),
										'\0'
										};

			if(strcmp(FolderName,"atlas/") == 0 || strcmp(FolderName,"ui_img") == 0  )
			{
				int j = 0;
			}
			else
			{
				pFile->ReleaseResource();
			}
		}
		else
		{
			pFile->ReleaseResource();
		}
	}
}

void			ResMan::GetTexMemory(unsigned int& TexC, unsigned int &TexMem, unsigned int &TexMemComp, unsigned int &TexMemFullC, unsigned int &TexMemPal, bool Output)
{
	TexC = TexMem = TexMemComp = TexMemFullC = TexMemPal = 0;
	unsigned int c= m_ResourceMap.GetElementC();
	std::vector<Surface*>		Surfaces;
	for (unsigned int i=0; i<c; i++)
	{
		ResourceFile*	pFile = m_ResourceMap.GetElementByIndex(i);
		if (pFile->GetFileType() != ResourceFile::RES_SURFACE) continue;
		if (pFile->GetState() != ResourceFile::RS_READY) continue;

		TexC++;
		Surface* pSurf = (Surface*)pFile;
		unsigned int LocalTexMem= 0, LocalTexMemComp= 0, LocalTexMemFullC= 0, LocalTexMemPal = 0;
		pSurf->GetTexMemory(LocalTexMem, LocalTexMemComp, LocalTexMemFullC, LocalTexMemPal);

		TexMem +=LocalTexMem;
		TexMemComp += LocalTexMemComp;
		TexMemFullC += LocalTexMemFullC;
		TexMemPal += LocalTexMemPal;
		if (Output)
		{
			Surfaces.push_back(pSurf);
		}
	}
	TexMemComp /=1024;
	TexMemFullC /=1024;
	TexMemPal /=1024;


	if (Output)
	{
		std::sort(Surfaces.begin(), Surfaces.end(), SortBySize);
		for (unsigned int i=0; i<Surfaces.size(); i++)
		{
			Surface* pSurf = Surfaces[i];
			_LOG(MSG_WARNING, "IMG " << pSurf->GetName() << "["<<pSurf->GetWidth() << "x" << pSurf->GetHeight()<<"], "<< pSurf->GetSize()/1024<<"kb"); 
		}
	}

}


void	ResMan::AddUIToMap(const std::string& Name)
{
	ResourceFile* res = GetResource(Name);
	m_UIResourceMap.AddElement(Name,res);
}

